/***************************************************************************
 *                                                                         *
 *   This file is part of the CrystalMem project,                          *
 *       http://code.google.com/p/crystalmem                               *
 *                                                                         *
 *   Copyright (C) 2008 by Alberto Scarpa <scarpa.alberto@gmail.com>       *
 *   Copyright (C) 2008 by Enrico Ros <enrico.ros@gmail.com>               *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#include "ProgramNode.h"

#define uRandom ((double)qrand() / (double)RAND_MAX)
#define kRandom (uRandom * 4.0 - 2.0)

static int nnn = 0;
ProgramNode::ProgramNode( quint64 retAddr, ProgramNode *_parent )
    : returnAddress( retAddr )
    , objAddress( 0 )
    , funcAddress( 0 )
    , localMem( 0 )
    , totalMem( 0 )
    , newPassCount( 0 )
    , deletePassCount( 0 )
    , idleCounter( 100 )
    , fixed( false )
    , pos( kRandom, kRandom, kRandom )
    , parent( _parent )
{
nnn++;
if ( (nnn % 1000) == 0 )
    qWarning("nodi: %d", nnn);

    // re-randomize point if module is too low
    while ( pos.module() < 0.1 )
        pos = Vector3( kRandom, kRandom, kRandom );
}

ProgramNode::~ProgramNode()
{
    qDeleteAll(children);
}

ProgramNode * ProgramNode::memoryAdded( QList<quint64> & backTrace, quint32 size )
{
    // recursion end
    if ( backTrace.isEmpty() ) {
        localMem += size;
        totalMem += size;
        newPassCount++;
        idleCounter = 100;
        return this;
    }

    // recursion body
    quint64 addr = backTrace.takeFirst();
    ProgramNode *next = 0;
    foreach(ProgramNode *n, children) {
        if ( n->returnAddress == addr ) {
            next = n;
            break;
        }
    }

    if ( !next ) {
        next = new ProgramNode( addr, this );
        next->pos += pos;
        next->vel += vel;
        children.append( next );
    }

    // recursion call
    ProgramNode *toRet = next->memoryAdded( backTrace, size );

    // post visit
    totalMem += size;
    newPassCount++;
    idleCounter = 200;
    return toRet;
}

void ProgramNode::memoryRemoved( quint32 size, ProgramNode *childToRemove )
{
    totalMem -= size;
    deletePassCount++;

    if ( childToRemove ) {
        delete childToRemove;
        children.removeAll( childToRemove );
    }

    // if root node
    if ( !parent )
        return;

    if ( totalMem == 0 )
        parent->memoryRemoved( size, this );
    else
        parent->memoryRemoved( size, 0 );
}

void ProgramNode::print()
{
    qDebug( "N: %lli", returnAddress );
    foreach(ProgramNode *n, children) {
        n->print();
    }
}
